// 导入所需的类和模块
import { Graph, Addon, FunctionExt, Shape } from '@antv/x6';
// 导入自定义的形状定义
import './shape';
// 导入图数据
import graphData from './data';

// import { styleToObject } from '@antv/x6/lib/util/dom/attr';

// 定义 FlowGraph 类
export default class FlowGraph {
  // 公有静态属性，用于存储 Graph 实例
  public static graph: Graph;
  // 私有静态属性，用于存储 Stencil 实例
  private static stencil: Addon.Stencil;

  // 初始化方法，用于创建和配置 Graph 实例
  public static init() {
    // 创建 Graph 实例
    this.graph = new Graph({
      // 设置容器元素
      container: document.getElementById('container')!,
      // // 设置画布宽度
      panning: {
        enabled: true,
        eventTypes: ['rightMouseDown'],
      },
      width: 1000,
      // 设置画布高度
      height: 700,
      // 配置网格
      grid: {
        size: 10, // 网格大小
        visible: true, // 网格可见
        type: 'doubleMesh', // 网格类型
        args: [
          {
            color: '#cccccc', // 网格颜色
            thickness: 1, // 网格厚度
          },
          {
            color: '#5F95FF', // 主网格颜色
            thickness: 1, // 主网格厚度
            factor: 4, // 主网格间隔因子
          },
        ],
      },

      // 配置滚动条
      scroller: {
        enabled: false, // 启用滚动条
        pageVisible: true, // 页面可见
        pageBreak: true, // 分页
        pannable: false, // 禁止拖动
      },
      // 配置鼠标滚轮缩放
      mousewheel: {
        enabled: true, // 启用鼠标滚轮
        // modifiers: ['ctrl', 'meta'], // 缩放修饰键
        minScale: 0.5, // 最小缩放比例
        maxScale: 2, // 最大缩放比例
      },

      // 配置节点选择
      selecting: {
        enabled: true, // 启用选择
        multiple: true, // 多选
        rubberband: true, // 橡皮筋选择
        movable: true, // 可移动
        showNodeSelectionBox: true, // 显示节点选择框
      },
      connecting: {
        anchor: 'center', // 连接点锚定在中心
        connectionPoint: 'anchor', // 连接点类型为锚点
        allowBlank: false, // 不允许空白连接
        highlight: true, // 高亮显示连接点
        snap: true, // 吸附到网格
        createEdge() {
          // 创建边的方法
          return new Shape.Edge({
            attrs: {
              line: {
                stroke: '#5F95FF', // 线条颜色
                strokeWidth: 1, // 线条宽度
                targetMarker: {
                  name: 'classic', // 目标标记类型
                  size: 8, // 目标标记大小
                },
              },
            },
            // 使用 manhattan 路由
            router: {
              name: 'manhattan',
              args: {
                padding: 10 // 路由线与节点的间距
              }
            },
            // 使用 smooth 连接器
            connector: {
              name: 'smooth'
            }
          });
        },
        validateConnection({ sourceView, targetView, sourceMagnet, targetMagnet }) {
          // 验证连接的方法
          if (sourceView === targetView) {
            return false; // 不允许连接到自身
          }
          if (!sourceMagnet) {
            return false; // 源节点必须有点
          }
          if (!targetMagnet) {
            return false; // 目标节点必须有点
          }
          return true; // 连接有效
        },
      },
      highlighting: {
        magnetAvailable: {
          name: 'stroke', // 高亮类型为描边
          args: {
            padding: 4, // 高亮区域的内边距
            attrs: {
              strokeWidth: 4, // 高亮描边宽度
              stroke: 'rgba(223,234,255)', // 高亮描边颜色
            },
          },
        },
      },
      snapline: true, // 启用对齐线
      history: true, // 启用历史记录
      clipboard: {
        enabled: true, // 启用剪贴板
      },
      keyboard: {
        enabled: true, // 启用键盘快捷键
      },
      embedding: {
        enabled: true, // 启用嵌入功能
        findParent({ node }) {

          // 查找父节点的方法
          const bbox = node.getBBox(); // 获取节点的边界框
          return this.getNodes().filter((node) => {
            // 只有 data.parent 为 true 的节点才是父节点
            const data = node.getData<any>();
            if (data && data.parent) {
              const targetBBox = node.getBBox(); // 获取目标节点的边界框
              return bbox.isIntersectWithRect(targetBBox); // 判断边界框是否相交
            }
            return false;
          });
        },
      },
    });
    this.initStencil(); // 初始化模板
    this.initShape(); // 初始化形状
    this.initGraphShape(); // 初始化图形形状
    this.initEvent(); // 初始化事件
    return this.graph; // 返回图形实例


    
  }

  /**
   * 定义一个私有静态方法initStencil，用于初始化模板图
   * @private
   * stencilGraphWidth 设置下方栏位宽度（整个大的div）
   * layoutOptions 布局配置
   *  columns 列数
   *  columnWidth 列宽（显示节点多少，节点的div）
   *  rowHeight 行高
   */
  private static initStencil() {
    // 创建一个新的Addon.Stencil实例，并传入配置对象
    this.stencil = new Addon.Stencil({
      // 设置模板图的目标图形为this.graph
      target: this.graph,
      // 定义模板图的宽度，单位为像素
      stencilGraphWidth: 10000,
      // 定义模板图的高度，单位为像素
      stencilGraphHeight: 600,
      // 定义一个名为groups的数组，用于存储不同类型的节点组配置
      groups: [
        {
          // 定义第四个节点组的名称为'sjctest'
          name: 'sjctest',
          // 设置该节点组的标题为'自定义节点'
          title: '全部节点',
          // 设置该节点组在图形中的高度
          graphHeight: 200,
          layoutOptions: {
            // 设置布局的列数
            columns: 12,
            // 设置布局的横向边距
            columnWidth: 120,
            // 设置布局的纵向边距
            rowHeight: 60,
            // marginX: 10,
          },
        },
        {
          // 定义第四个节点组的名称为'sjctest'
          name: 'tynl',
          // 设置该节点组的标题为'自定义节点'
          title: '通用能力',
          // 设置该节点组在图形中的高度
          graphHeight: 200,
          layoutOptions: {
            // 设置布局的列数
            columns: 12,
            // 设置布局的横向边距
            columnWidth: 120,
            // 设置布局的纵向边距
            rowHeight: 60,
            // marginX: 10,
          },
        },
        {
          // 定义第四个节点组的名称为'sjctest'
          name: 'kshzs',
          // 设置该节点组的标题为'自定义节点'
          title: '可视化展示',
          // 设置该节点组在图形中的高度
          graphHeight: 200,
          layoutOptions: {
            // 设置布局的列数
            columns: 12,
            // 设置布局的横向边距
            columnWidth: 120,
            // 设置布局的纵向边距
            rowHeight: 60,
            // marginX: 10,
          },
        },
        {
          // 定义第四个节点组的名称为'sjctest'
          name: 'zbrwlc',
          // 设置该节点组的标题为'自定义节点'
          title: '装备任务流程',
          // 设置该节点组在图形中的高度
          graphHeight: 200,
          layoutOptions: {
            // 设置布局的列数
            columns: 12,
            // 设置布局的横向边距
            columnWidth: 120,
            // 设置布局的纵向边距
            rowHeight: 60,
            // marginX: 10,
          },
        },
      ],
    });
    const stencilContainer = document.querySelector('#stencil');
    stencilContainer?.appendChild(this.stencil.container);
  }

  // 定义一个私有静态异步方法 initShape，用于初始化图形中的节点
  private static async initShape() {
    // 从当前类的上下文中获取 graph 对象，graph 对象用于操作图形
    const { graph } = this;
    // 使用动态导入语法，异步加载 sjctest 模块中的 sjcNodes
    const { sjcNodes } = await import('./shapes/sjctest');
    // 使用动态导入语法，异步加载 tynl 模块中的 tynlNodes
    const { tynlNodes } = await import('./shapes/tynl');
    
    // 定义一个函数 createNodes，接受一个配置数组，并使用 graph.createNode 方法创建节点实例
    const createNodes = (configs: any[]) => configs.map(config => graph.createNode(config));
  
    // 创建一个自定义节点，并设置其属性
    // 动态创建节点
    const sjcNodesInstances = createNodes(sjcNodes);
    const tynlNodesInstances = createNodes(tynlNodes);
      
    // 加载节点到模板图中
    this.stencil.load([...sjcNodesInstances], 'sjctest');
    this.stencil.load(tynlNodesInstances, 'tynl');
    // this.stencil.load([sjc1, sjc2, sjc3, sjc4, sjc5, sjc6, sjc7, sjc8, sjc9,], 'kshzs');
    // this.stencil.load([sjc1, sjc2, sjc3, sjc4, sjc5, sjc6, sjc7, sjc8, sjc9,], 'zbrwlc');
  }

  // 定义一个私有静态方法 initGraphShape 用于初始化图形
  private static initGraphShape() {
    // 调用 graph 对象的 fromJSON 方法，将 graphData 作为参数传入
    // 这里使用了类型断言 'as any'，表示将 graphData 视为任意类型
    // 这通常用于绕过类型检查，但在实际开发中应尽量避免，以确保类型安全
    this.graph.fromJSON(graphData as any);
  }

  // 定义一个私有静态方法 showPorts，用于显示或隐藏一组SVG元素
  private static showPorts(ports: NodeListOf<SVGAElement>, show: boolean) {
    // 使用for循环遍历传入的NodeList<SVGAElement>对象
    for (let i = 0, len = ports.length; i < len; i = i + 1) {
      // 根据show参数的值，设置每个SVG元素的可见性
      // 如果show为true，则设置可见性为'visible'
      // 如果show为false，则设置可见性为'hidden'
      ports[i].style.visibility = show ? 'visible' : 'hidden';
    }
  }

  // 定义一个私有静态方法 initEvent，用于初始化事件监听
  private static initEvent() {
    // 从当前类中获取 graph 对象
    const { graph } = this;
    // 获取页面中 id 为 'container' 的 DOM 元素
    const container = document.getElementById('container')!;

    // 监听 graph 上的 'node:contextmenu' 事件，当节点被右键点击时触发
    graph.on('node:contextmenu', ({ cell, view }) => {
      // 打印节点视图的容器
      console.log(view.container);
      // 获取节点文本属性的当前文本内容
      const oldText = cell.attr('text/text') as string;
      // 设置节点文本属性的显示样式为 'none'，隐藏文本
      cell.attr('text/style/display', 'none');
      // 在节点视图中查找类名为 'x6-edit-text' 的元素
      const elem = view.container.querySelector('.x6-edit-text') as HTMLElement;
      // 如果找到了该元素
      if (elem) {
        // 将元素的文本内容设置为之前获取的旧文本
        elem.innerText = oldText;
        // 使元素获得焦点
        elem.focus();
      }
      // 定义一个失去焦点时的回调函数
      const onBlur = () => {
        // 将节点的文本属性设置为当前元素的文本内容
        cell.attr('text/text', elem.innerText);
      };
      // 如果找到了该元素
      if (elem) {
        // 监听元素的 'blur' 事件，当元素失去焦点时执行回调函数
        elem.addEventListener('blur', () => {
          onBlur();
          // 移除元素的 'blur' 事件监听，避免重复执行
          elem.removeEventListener('blur', onBlur);
        });
      }
    });
    // 监听 graph 上的 'node:mouseenter' 事件，当鼠标进入节点时触发
    graph.on(
      'node:mouseenter',
      // 使用 FunctionExt.debounce 方法进行防抖处理，延迟执行
      FunctionExt.debounce(() => {
        // 获取容器中所有类名为 'x6-port-body' 的元素
        const ports = container.querySelectorAll('.x6-port-body') as NodeListOf<SVGAElement>;
        // 调用 showPorts 方法显示端口
        this.showPorts(ports, true);
      }),
      500, // 防抖延迟时间为 500 毫秒
    );
    // 监听 graph 上的 'node:mouseleave' 事件，当鼠标离开节点时触发
    graph.on('node:mouseleave', () => {
      // 获取容器中所有类名为 'x6-port-body' 的元素
      const ports = container.querySelectorAll('.x6-port-body') as NodeListOf<SVGAElement>;
      // 调用 showPorts 方法隐藏端口
      this.showPorts(ports, false);
    });

    // 监听 graph 上的 'node:collapse' 事件，当节点折叠时触发
    graph.on('node:collapse', ({ node, e }: any) => {
      // 阻止事件冒泡
      e.stopPropagation();
      // 切换节点的折叠状态
      node.toggleCollapse();
      // 获取节点所有子节点
      const collapsed = node.isCollapsed();
      const cells = node.getDescendants();
      // 遍历子节点
      cells.forEach((n: any) => {
        // 根据折叠状态显示或隐藏子节点
        if (collapsed) {
          n.hide();
        } else {
          n.show();
        }
      });
    });
    // delete按钮
    graph.bindKey('delete', () => {
      const cells = graph.getSelectedCells();
      if (cells.length) {
        graph.removeCells(cells);
      }
    });
  }
}
